SelectSQLBuilderFactory.java
package org.codefilarete.stalactite.query.builder;
import org.codefilarete.stalactite.query.builder.FunctionSQLBuilderFactory.FunctionSQLBuilder;
import org.codefilarete.stalactite.query.model.Query;
import org.codefilarete.stalactite.query.model.Select;
import org.codefilarete.stalactite.query.model.Selectable;
import org.codefilarete.stalactite.query.model.operator.SQLFunction;
import org.codefilarete.stalactite.sql.ddl.JavaTypeToSqlTypeMapping;
import org.codefilarete.tool.Reflections;
import org.codefilarete.tool.Strings;
/**
* Factory for {@link SelectSQLBuilder}. It's overridable through {@link org.codefilarete.stalactite.sql.Dialect}
* which then let one gives its own implementation of {@link SelectSQLBuilderFactory}.
*
* @author Guillaume Mary
* @see #queryBuilder(Select, DMLNameProvider)
*/
public class SelectSQLBuilderFactory {
private final FunctionSQLBuilderFactory functionSQLBuilderFactory;
/**
* A default constructor that uses default factories, use mainly for test or default behavior (not related to a database vendor)
*
* @param javaTypeToSqlTypeMapping a {@link Query}
*/
public SelectSQLBuilderFactory(JavaTypeToSqlTypeMapping javaTypeToSqlTypeMapping) {
this(new FunctionSQLBuilderFactory(javaTypeToSqlTypeMapping));
}
public SelectSQLBuilderFactory(FunctionSQLBuilderFactory functionSQLBuilderFactory1) {
this.functionSQLBuilderFactory = functionSQLBuilderFactory1;
}
public SelectSQLBuilder queryBuilder(Select select, DMLNameProvider dmlNameProvider) {
return new SelectSQLBuilder(select, dmlNameProvider, this.functionSQLBuilderFactory.functionSQLBuilder(dmlNameProvider));
}
/**
* Formats {@link Select} object to an SQL {@link String} through {@link #toSQL()}.
*
* @author Guillaume Mary
* @see #toSQL()
*/
public static class SelectSQLBuilder implements SQLBuilder {
private final Select select;
private final DMLNameProvider dmlNameProvider;
private final FunctionSQLBuilder functionSQLBuilder;
public SelectSQLBuilder(Select select, DMLNameProvider dmlNameProvider, FunctionSQLBuilder functionSQLBuilder) {
this.select = select;
this.dmlNameProvider = dmlNameProvider;
this.functionSQLBuilder = functionSQLBuilder;
}
@Override
public String toSQL() {
StringSQLAppender result = new StringSQLAppender(dmlNameProvider);
result.catIf(this.select.isDistinct(), "distinct ");
cat(this.select, result);
return result.getSQL();
}
private void cat(Iterable<? extends Selectable<?> /* String, Column or AliasedColumn */> select, StringSQLAppender sql) {
for (Object o : select) {
if (o instanceof SQLFunction) {
cat((SQLFunction) o, sql);
} else if (o instanceof Selectable) { // must be after previous ifs because they deal with dedicated Selectable cases
cat((Selectable) o, sql);
} else if (o instanceof Iterable) {
cat((Iterable<? extends Selectable<?> /* String, Column or AliasedColumn */>) o, sql);
} else {
throw new UnsupportedOperationException("Operator " + Reflections.toString(o.getClass()) + " is not implemented");
}
sql.cat(", ");
}
if (Strings.tail(sql.getSQL(), 2).equals(", ")) { // if not, means select was empty
// cut the trailing comma
sql.removeLastChars(2);
}
}
protected void cat(Selectable<?> column, StringSQLAppender sql) {
String alias = select.getAliases().get(column);
sql.catColumn(column).catIf(!Strings.isEmpty(alias), " as " + alias);
}
private void cat(SQLFunction<?, ?> operator, StringSQLAppender appenderWrapper) {
String alias = select.getAliases().get(operator); // can be UnitaryOperator which is Selectable
functionSQLBuilder.cat(operator, appenderWrapper);
appenderWrapper.catIf(!Strings.isEmpty(alias), " as " + alias);
}
}
}